{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dde31462",
   "metadata": {},
   "outputs": [],
   "source": [
    "# --- 数据处理与文件操作 ---\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "import os\n",
    "from tqdm import tqdm # 用于显示优雅的进度条\n",
    "\n",
    "# --- 机器学习：模型、划分与评估 ---\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.ensemble import RandomForestClassifier\n",
    "from sklearn.metrics import accuracy_score, classification_report, confusion_matrix\n",
    "\n",
    "# --- 数据可视化 ---\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "from matplotlib.font_manager import FontProperties\n",
    "\n",
    "print(\"所有库导入成功。\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "06853cae",
   "metadata": {},
   "outputs": [],
   "source": [
    "# --- 定义中文字体路径 ---\n",
    "font_path = 'SimHei.ttf'\n",
    "\n",
    "# --- 检查字体文件是否存在 ---\n",
    "if os.path.exists(font_path):\n",
    "    cn_font = FontProperties(fname=font_path)\n",
    "    plt.rcParams['font.family'] = 'sans-serif'\n",
    "    plt.rcParams['font.sans-serif'] = [cn_font.get_name()]\n",
    "    plt.rcParams['axes.unicode_minus'] = False  # 解决负号'-'显示为方块的问题\n",
    "    print(f\"中文字体 '{cn_font.get_name()}' 加载成功，图表将支持中文显示。\")\n",
    "else:\n",
    "    print(f\"警告：找不到字体文件 '{font_path}'。图表中的中文可能无法正常显示。\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6ea451f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "def extract_features_from_file(file_path, label, max_frames=2000):\n",
    "    \"\"\"\n",
    "    从单个CSV文件中读取数据，提取特征，并返回一个包含这些特征的DataFrame。\n",
    "    \"\"\"\n",
    "    try:\n",
    "        # 读取指定行数的数据\n",
    "        df = pd.read_csv(file_path, nrows=max_frames)\n",
    "\n",
    "        # 如果文件为空或数据不足，则跳过\n",
    "        if df.empty or len(df) < 100: # 设定一个阈值，如少于100帧的数据意义不大\n",
    "            return None\n",
    "\n",
    "        features = {}\n",
    "        # 1. 视线数据 (Gaze) 的统计特征\n",
    "        features['gaze_x_mean'] = df['Gaze_X'].mean()\n",
    "        features['gaze_y_mean'] = df['Gaze_Y'].mean()\n",
    "        features['gaze_x_std'] = df['Gaze_X'].std()\n",
    "        features['gaze_y_std'] = df['Gaze_Y'].std()\n",
    "\n",
    "        # 2. 表情数据 (Expression) 的频率特征\n",
    "        expression_counts = df['Expression'].value_counts(normalize=True)\n",
    "        for expression, frequency in expression_counts.items():\n",
    "            features[f'expr_{expression}'] = frequency\n",
    "\n",
    "        # 3. 添加标签\n",
    "        features['label'] = label\n",
    "        return pd.DataFrame([features])\n",
    "\n",
    "    except Exception as e:\n",
    "        # 捕获其他可能的读取或处理错误\n",
    "        # print(f\"处理文件 {file_path} 时发生错误: {e}\") # 如果需要详细调试，可以取消此行注释\n",
    "        return None\n",
    "\n",
    "print(\"特征提取函数 'extract_features_from_file' 定义完成。\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0fd2d1b2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# --- 自动发现文件 ---\n",
    "asd_folder = 'ASD'\n",
    "td_folder = 'TD'\n",
    "file_list = []\n",
    "\n",
    "if os.path.exists(asd_folder):\n",
    "    for filename in os.listdir(asd_folder):\n",
    "        if filename.endswith('.csv'):\n",
    "            file_list.append((os.path.join(asd_folder, filename), 1)) # 1 代表 ASD\n",
    "\n",
    "if os.path.exists(td_folder):\n",
    "    for filename in os.listdir(td_folder):\n",
    "        if filename.endswith('.csv'):\n",
    "            file_list.append((os.path.join(td_folder, filename), 0)) # 0 代表 TD\n",
    "\n",
    "print(f\"数据发现完成，总共找到 {len(file_list)} 个样本文件。\")\n",
    "\n",
    "# --- 循环处理所有文件，提取特征 (带进度条) ---\n",
    "all_features_list = [extract_features_from_file(fp, lbl) for fp, lbl in tqdm(file_list, desc=\"正在处理文件\")]\n",
    "\n",
    "# --- 合并数据并进行预处理 ---\n",
    "# 过滤掉处理失败的None值\n",
    "all_features_list = [f for f in all_features_list if f is not None]\n",
    "\n",
    "if all_features_list:\n",
    "    # 使用 concat 合并，并设置 sort=False 以避免 FutureWarning\n",
    "    full_dataset = pd.concat(all_features_list, ignore_index=True, sort=False)\n",
    "    # 对于某些受试者可能从未出现的表情，其特征值为NaN，我们用0填充\n",
    "    full_dataset = full_dataset.fillna(0)\n",
    "\n",
    "    print(f\"\\n特征工程完成！成功处理了 {full_dataset.shape[0]} 个有效样本。\")\n",
    "    print(\"最终特征数据集预览:\")\n",
    "    print(full_dataset.head())\n",
    "else:\n",
    "    print(\"\\n错误：未能从任何文件中成功提取特征。请检查CSV文件内容和格式。\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6a51cbfc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 确保所有必要的库已导入\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.font_manager as fm\n",
    "import seaborn as sns\n",
    "from sklearn.metrics import accuracy_score, classification_report, confusion_matrix, roc_curve, roc_auc_score\n",
    "\n",
    "# ===================================================================\n",
    "# 1. 中文字体设置 (增强版)\n",
    "# ===================================================================\n",
    "print(\"--- 正在设置中文字体 ---\")\n",
    "# 定义字体文件路径\n",
    "font_path = 'SimHei.ttf'\n",
    "\n",
    "if os.path.exists(font_path):\n",
    "    # 清空并重建字体缓存，这可能需要一些时间\n",
    "    # 第一次运行后，可能需要重启Jupyter Notebook的内核(Kernel -> Restart)才能生效\n",
    "    try:\n",
    "        fm._rebuild()\n",
    "    except:\n",
    "        # 在某些环境中 _rebuild() 是私有方法，可能会有警告，可以忽略\n",
    "        pass\n",
    "    \n",
    "    # 将字体文件加载为字体属性对象\n",
    "    cn_font = fm.FontProperties(fname=font_path)\n",
    "    \n",
    "    # 设置全局字体（以防万一）\n",
    "    plt.rcParams['font.family'] = cn_font.get_name()\n",
    "    plt.rcParams['axes.unicode_minus'] = False  # 解决负号'-'显示为方块的问题\n",
    "    \n",
    "    print(f\"中文字体 '{cn_font.get_name()}' 加载成功。如果第一次显示仍有问题，请尝试重启内核。\")\n",
    "else:\n",
    "    # 如果找不到字体文件，创建一个空的字体属性以避免代码错误\n",
    "    cn_font = fm.FontProperties()\n",
    "    print(f\"警告：找不到字体文件 '{font_path}'。图表中的中文将无法正常显示。\")\n",
    "\n",
    "\n",
    "# ===================================================================\n",
    "# 2. 开始模型评估与可视化\n",
    "# ===================================================================\n",
    "if 'rf_model' in locals():\n",
    "    print(\"\\n\\n=============== 随机森林 评估结果 ===============\")\n",
    "    y_pred = rf_model.predict(X_test)\n",
    "    y_pred_proba = rf_model.predict_proba(X_test)[:, 1]\n",
    "\n",
    "    print(f\"准确率: {accuracy_score(y_test, y_pred):.4f}\")\n",
    "    print(\"\\n分类报告:\")\n",
    "    print(classification_report(y_test, y_pred, target_names=['正常发育 (TD)', '孤独症 (ASD)']))\n",
    "    \n",
    "    # 绘制混淆矩阵\n",
    "    cm = confusion_matrix(y_test, y_pred)\n",
    "    plt.figure(figsize=(8, 6))\n",
    "    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=['TD', 'ASD'], yticklabels=['TD', 'ASD'])\n",
    "    plt.title('混淆矩阵', fontproperties=cn_font, fontsize=16)\n",
    "    plt.ylabel('真实类别', fontproperties=cn_font, fontsize=12)\n",
    "    plt.xlabel('预测类别', fontproperties=cn_font, fontsize=12)\n",
    "    plt.show()\n",
    "\n",
    "    # 绘制 ROC 曲线\n",
    "    fpr, tpr, thresholds = roc_curve(y_test, y_pred_proba)\n",
    "    auc_score = roc_auc_score(y_test, y_pred_proba)\n",
    "    print(f\"\\n模型的 AUC 值为: {auc_score:.4f}\")\n",
    "\n",
    "    plt.figure(figsize=(8, 6))\n",
    "    plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC 曲线 (面积 = {auc_score:.2f})')\n",
    "    plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')\n",
    "    plt.xlim([0.0, 1.0])\n",
    "    plt.ylim([0.0, 1.05])\n",
    "    plt.xlabel('假正率 (False Positive Rate)', fontproperties=cn_font, fontsize=12)\n",
    "    plt.ylabel('真正率 (True Positive Rate)', fontproperties=cn_font, fontsize=12)\n",
    "    plt.title('接收者操作特征 (ROC) 曲线', fontproperties=cn_font, fontsize=16)\n",
    "    plt.legend(prop=cn_font) # 在图例中也使用中文字体\n",
    "    plt.show()\n",
    "    \n",
    "    # 绘制预测概率分布图 (已修正 'fill' -> 'shade')\n",
    "    df_proba = pd.DataFrame({'true_label': y_test, 'pred_proba_asd': y_pred_proba})\n",
    "    plt.figure(figsize=(10, 6))\n",
    "    sns.kdeplot(df_proba[df_proba['true_label']==0]['pred_proba_asd'], label='正常发育 (TD)', shade=True)\n",
    "    sns.kdeplot(df_proba[df_proba['true_label']==1]['pred_proba_asd'], label='孤独症 (ASD)', shade=True)\n",
    "    plt.title('模型预测为ASD的概率分布', fontproperties=cn_font, fontsize=16)\n",
    "    plt.xlabel('预测为“孤独症(ASD)”的概率', fontproperties=cn_font, fontsize=12)\n",
    "    plt.ylabel('密度', fontproperties=cn_font, fontsize=12)\n",
    "    plt.legend(prop=cn_font)\n",
    "    plt.show()\n",
    "\n",
    "    # 绘制关键特征分布对比图\n",
    "    feature_importances = pd.DataFrame({'feature': X.columns, 'importance': rf_model.feature_importances_})\n",
    "    feature_importances = feature_importances.sort_values('importance', ascending=False)\n",
    "    top_2_features = feature_importances['feature'].head(2).tolist()\n",
    "    \n",
    "    print(\"\\n可视化两个最重要特征在不同组别中的数据分布:\")\n",
    "    print(f\"  - 特征1: {top_2_features[0]}, 特征2: {top_2_features[1]}\")\n",
    "\n",
    "    plot_data = pd.concat([X, y], axis=1)\n",
    "\n",
    "    plt.figure(figsize=(12, 6))\n",
    "    for i, feature in enumerate(top_2_features):\n",
    "        plt.subplot(1, 2, i+1)\n",
    "        sns.violinplot(x='label', y=feature, data=plot_data, palette='muted')\n",
    "        plt.title(f'特征 \"{feature}\" 的分布对比', fontproperties=cn_font, fontsize=14)\n",
    "        plt.ylabel('特征值', fontproperties=cn_font)\n",
    "        plt.xlabel('组别', fontproperties=cn_font)\n",
    "        plt.xticks(ticks=[0, 1], labels=['正常发育 (TD)', '孤独症 (ASD)'], fontproperties=cn_font)\n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "\n",
    "else:\n",
    "    print(\"\\n模型未训练，无法进行评估。\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "52cda73d",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "894e8502",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "wangzhibo202352320101",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.8.19"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
